#include <cstdio>
#include <cstdlib>
//#include <tchar.h>

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/video/video.hpp"
#include <opencv2/gpu/gpu.hpp>


#include "VideoRead.h"

#include "FeatureComputer.h"

#include "Classifier.h"

#include "Bagging.h"

#include "ClassifierRank.h"



class LcInterface
{
public:

	LcInterface();
	void input( int argc, char * const argv[]);

	LcClassifier*  getClassifier(){ return my_classifier;}

	LcVideoReadExt getTrainVideo();
	LcVideoReadExt getTestVideo();


	bool IF_TRAIN;
	bool IF_TEST;

	int train_f_start;
	int train_f_end;
	int train_f_rate;

	int test_f_start;
	int test_f_end;
	int test_f_rate;
	
	int cluster_n;

	string root;

	string train_video_name;
	string test_video_name;

	string feature_code_string;

	string classifier_name;

	void buildDatasetmap();

	map< string , Vec3i> data_map;

	bool setVideo( string name ,int & start, int & end , int & rate);
	void setClassifier();

	void init();

	LcClassifier* my_classifier;

	int n_cluster_avg;
	int n_dim_feature_selection;
	string feature_selection_logfile;
	int bagging_num;

	int instance_limit;

	bool IF_KMEAN;

};

LcVideoReadExt LcInterface::getTrainVideo()
{
	LcVideoReadExt ans;
	ans.video_name = train_video_name;
	ans.f_start = train_f_start;
	ans.f_end = train_f_end;
	ans.f_rate = train_f_rate;
	ans.t_win = 0;
	ans.root = root;
	return ans;
}

LcVideoReadExt LcInterface::getTestVideo()
{
	LcVideoReadExt ans;
	ans.video_name = test_video_name;
	ans.f_start = test_f_start;
	ans.f_end = test_f_end;
	ans.f_rate = test_f_rate;
	ans.t_win = 0;
	ans.root = root;
	return ans;
}

void LcInterface::setClassifier()
{
	if(classifier_name == "rdt")
	{
		my_classifier = new LcRandomTrees;
	}
}
void LcInterface::init()
{
	if(IF_TEST == false)
	{
		cout << " into " << endl;
		if(train_f_start<0) train_f_start = test_f_start;
		if(train_f_end<0) train_f_end = test_f_end;
		if(train_f_rate<0) train_f_rate = test_f_rate;
	}

	if(feature_code_string == "") feature_code_string = "l";

	if(classifier_name == "" ) classifier_name = "rdt";
	setClassifier();

	if(train_video_name == "" ){train_video_name = "GOPR2382_cab";}
	setVideo( train_video_name, train_f_start, train_f_end, train_f_rate);

	if(test_video_name == "" ){test_video_name = "GOPR2384_cab";}
	setVideo( test_video_name, test_f_start, test_f_end, test_f_rate);
}

LcInterface::LcInterface()
{
	train_f_start = -1;
	train_f_rate = -1;
	train_f_end = -1;

	test_f_start = -1;
	test_f_rate = -1;
	test_f_end = -1;

	root = "./../_FGS/";
	train_video_name = "";
	test_video_name = "";
	feature_code_string = "";
	classifier_name = "";
	
	

	IF_TEST = false;
	IF_TRAIN = false;

	IF_KMEAN = true;

	instance_limit = 1e4;

	cluster_n = -1; //using default if <0
	n_cluster_avg = -1; //using default if <0
	n_dim_feature_selection = -1;//using all in log_file if <0
	feature_selection_logfile = "./select_id.txt";
	bagging_num = -1; //using default if <0

	buildDatasetmap();
}

void LcInterface::buildDatasetmap()
{
	data_map.clear();
	data_map.insert( pair< string, Vec3i>( "GOPR2382_cab", Vec3i(150,5300,50)) );
	data_map.insert( pair< string, Vec3i>( "GOPR2384_cab", Vec3i(150,11200,25)) );
}

bool LcInterface::setVideo( string name ,int & start, int & end , int & rate)
{
	map<string , Vec3i>::iterator iter = data_map.find( name );
	if( iter != data_map.end())
	{
		Vec3i temp = iter->second;
		if(start<0)start = temp[0];
		if(end<0)end = temp[1];
		if(rate<0)rate = temp[2];
		return true;
	}
	else
	{
		cout << "One of Video is not in default" << endl;
		return false;
	}
}

void LcInterface::input( int argc, char * const argv[])
{
	for(int ind = 1;ind < argc;ind++)
	{
		/*** Parse switches ***/
		if (argv[ind][0] == '-') {
			switch (argv[ind][1]) {
				case 'f':
					root = string(argv[++ind]);
					break;
				case 't':
					train_video_name = string(argv[++ind]);
					break;
				case 'p':
					test_video_name = string(argv[++ind]);
					break;
				case 'v':
					feature_code_string =  string(argv[++ind]);
					break;
				case 'a':
					IF_TRAIN = true;
					break;
				case 'b':
					IF_TEST = true;
					break;
				case 's':
					//only for predict video setting , for trainning video it's always solid with frame
					// unless it's no testing model then the test frame setting will be use
					// as training parameter
					test_f_start =  atoi(argv[++ind]);
					break;
				case 'e':
					test_f_end = atoi(argv[++ind]);
					break;
				case 'r':
					test_f_rate = atoi(argv[++ind]);
					break;
				case 'c':
					classifier_name = string(argv[++ind]);
					break;
				case 'k':
					cluster_n = atoi(argv[++ind]);
					break;				
				case 'j':
					n_cluster_avg = atoi(argv[++ind]); //using default if <0
					break;
				case 'm':
					n_dim_feature_selection = atoi(argv[++ind]);//using all in log_file if <0
					break;
				case 'l':
					feature_selection_logfile = string(argv[++ind]);;
					break;
				case 'n':
					bagging_num = atoi(argv[++ind]); //using default if <0
					break;

				default : printf("Unknown switch '%c'\n", argv[ind][1]);
				break;
			}
		}
	}

	init();
}


int main(int argc, char * const argv[])
{	
	LcInterface my_setting;

	my_setting.input( argc, argv);

	//char *const setting_string[] ={ "","-b","-v","lb","-t","GOPR2382_cab","-s", "150", "-e", "5300", "-r","50","-p","GOPR2384_cab","-f","./../../_FGS/"};

	//char *const setting_string[] ={ "","-a","-t","GOPR2384_cab","-s", "175", "-e", "800", "-r","25","-p","GOPR2384_cab","-f","./../../_FGS/","-j","1"};	

	//my_setting.input( 13 + 1 ,setting_string);

	Kmeans my_crank;

	LcVideoReadExt my_reader = my_setting.getTrainVideo();

	my_crank.setReader( &my_reader);

	my_crank.setClassifier( my_setting.getClassifier() );

	vector<int> selected_id; selected_id.clear();

	LocalSensitiveBagging crank_bagging;

	bool IF_KMEAN = my_setting.IF_KMEAN;

	{
		
		{
			FILE * fin = fopen( my_setting.feature_selection_logfile.c_str(), "r");

			int n; 
			fscanf(fin, "%d" , &n);
			selected_id.resize( n) ;

			for(int i = 0 ; i < n ; i++) fscanf( fin, "%d" , &selected_id[i]);

			char _feature_string[400];

			if(fscanf( fin , "%s", _feature_string))
			{
				my_setting.feature_code_string = string(_feature_string);
			}

			
		}

		my_crank.setFeatureCode( my_setting.feature_code_string.c_str() );
		crank_bagging.setFeatureCode( my_setting.feature_code_string.c_str() );

		if(my_setting.cluster_n>0)
			my_crank.cluster_n = my_setting.cluster_n;

		{
			FILE * fid = fopen("bagging_config.txt","r");
			int n_classifier,instance_limit;

			if(!fscanf(fid, "%d%d",&n_classifier, &instance_limit))
			{
				n_classifier = 3;instance_limit = 10000;
			}

			fclose(fid);

			my_setting.instance_limit = instance_limit;

			if(my_setting.bagging_num<0) my_setting.bagging_num = n_classifier;
		}

		if(my_setting.n_cluster_avg > 0)
			crank_bagging.knn = my_setting.n_cluster_avg;

		if(my_setting.n_dim_feature_selection > 0)
			selected_id.resize( my_setting.n_dim_feature_selection );		
	}	

	if(my_setting.IF_TEST)
	{		
		crank_bagging.setReader( &my_reader );

		{
			string saving_name = my_reader.root + "classifier_output/crank/kmean_";
			crank_bagging.load( saving_name.c_str() );
		}
		
		crank_bagging.selected_id = selected_id;

		for(int i=0;i< (int)crank_bagging.h.size();i++)
			crank_bagging.h[i]->selected_id = selected_id;

		LcVideoReadExt test_video = my_setting.getTestVideo();

		cout << test_video.video_name << endl;

		LcHistogram global_extractor;

		LcValidator validator = crank_bagging.predict( test_video, global_extractor );

		{
			stringstream ss; ss << my_setting.cluster_n;
			
			stringstream ss2; ss2 << my_setting.n_dim_feature_selection;
			
			string report_name = "./report/kmean_report_" + my_setting.train_video_name + "_on_"
				+ my_setting.test_video_name + "_v_" + my_setting.feature_code_string + "_feat" + ss2.str() +  "_" + ss.str() + ".txt";
			FILE * fout = fopen( report_name.c_str(),"w");

				fprintf(fout,"%f\t%f\t%f\t%f\t%f\n", validator.getF1(),
					validator.tp,validator.fp,validator.fn,validator.tn);

			fclose(fout);
		}		

	}

	if(my_setting.IF_TRAIN)
	{

		my_crank.cluster();

		int n_classifier = my_setting.bagging_num;
		int instance_limit = my_setting.instance_limit;	

		crank_bagging.set( my_crank, n_classifier, instance_limit);

		for(int i=0;i< (int)crank_bagging.h.size();i++)
			crank_bagging.h[i]->selected_id = selected_id;

		crank_bagging.train();

		{
			string saving_name = my_reader.root + "classifier_output/crank/kmean_";
			crank_bagging.save( saving_name.c_str() );
		}


	}
	
	//{
	//	my_reader.root = "./../../_FGS/";
	//	my_reader.video_name = "GOPR2384_cab";
	//	my_reader.f_start = 175;
	//	my_reader.f_end = 11150;
	//	my_reader.f_rate = 25;

	//	my_reader.veb = 1;

	//	my_crank.setReader( &my_reader);
	//}

	//LcFeatureExtractor my_extractor;

	//string feature_code = "l"; // "robvltdmchsug"

	//{
	//	my_crank.setFeatureCode( feature_code.c_str() );
	//}

	//LcRandomTrees my_classifier;

	//{
	//	my_classifier.veb = 1;
	//	my_crank.setClassifier( &my_classifier);
	//}

	//if(0){
	//	my_crank.cluster();

	//	LocalSensitiveBagging crank_bagging;

	//	crank_bagging.setFeatureCode("l");

	//	crank_bagging.set( my_crank, 1, 500000);

	//	crank_bagging.train();

	//	{
	//		string saving_name = my_reader.root + "classifier_output/crank/";
	//		crank_bagging.save( saving_name.c_str() );
	//	}
	//}
	//else
	//{
	//	LocalSensitiveBagging crank_bagging;

	//	crank_bagging.setFeatureCode("l");

	//	crank_bagging.setReader( &my_reader);

	//	{
	//		string saving_name = my_reader.root + "classifier_output/crank/";
	//		crank_bagging.load( saving_name.c_str() );
	//	}
	//}

	//system("pause");
}